package shims

import "bytes"
import "unicode"
import "substr"

//Equal is a []byte that is a Matcher, Finder, and Replacer. As a Matcher, it
//returns whether the string is equal to Equal's underlying byte string.
//As a Finder, it returns all substrings equal to its underlying byte string.
//As a Replacer, it rewrites any string to its underlying byte string.
type Equal []byte

func (e Equal) Match(s []byte) bool {
	return bytes.Equal(e, s)
}

//Replace every byte string with e
func (e Equal) Replace(s []byte) []byte {
	return s
}

//Find at most n instances of nonoverlapping e in s
func (e Equal) Find(s []byte, n int) *substr.S {
	ret := substr.New(s, n)
	all := n < 0
	if all {
		n = len(s)
	}
	if el := len(e); el != 0 {
		c := e[0]
		for i, m := 0, 0; i < len(s) && m < n; i++ {
			if s[i] == c && (el == 1 || bytes.Equal(e, s[i:i+el])) {
				ret.Push(i, i+el)
				i += el - 1
				m++
			}
		}
	} else {
		for i := 0; i < len(s) && i < n; i++ {
			ret.Push(i, i)
		}
	}
	return ret.PushRest(!all)
}

//A matcher that matches any string that contains at least one instance of
//its underlying []byte
type Has []byte

func (h Has) Match(s []byte) bool {
	return bytes.Index(s, h) >= 0
}

//A matcher that matches any string that contains at least one instance of
//any of the characters in its underlying string
type HasAny string

func (h HasAny) Match(s []byte) bool {
	return bytes.IndexAny(s, string(h)) >= 0
}

//A Prefix is a []byte that is a Matcher and a Replacer. As a Matcher, it
//matches if the string is prefixed by the underlying string. As a Replacer, it
//prepends its underlying string onto its argument.
type Prefix []byte

func (p Prefix) Match(s []byte) bool {
	return bytes.HasPrefix(s, p)
}

func (p Prefix) Replace(s []byte) []byte {
	r := make([]byte, len(p)+len(s))
	copy(r[:len(p)], p)
	copy(r[len(p):], s)
	return r
}

//A Suffix is a []byte that is a Matcher and a Replacer. As a Matcher, it
//matches if the string is suffixed by the underlying string. As a Replacer, it
//appends its underlying string onto its argument.
type Suffix []byte

func (sf Suffix) Match(s []byte) bool {
	return bytes.HasSuffix(s, sf)
}

func (sf Suffix) Replace(s []byte) []byte {
	r := make([]byte, len(s)+len(sf))
	copy(r[:len(s)], s)
	copy(r[len(s):], sf)
	return r
}

//provides a finder like byte.Split
type Sep []byte

func (p Sep) Find(s []byte, n int) *substr.S {
	//essentially a copy of bytes.Split
	if len(p) == 0 {
		return Runes.Find(s, n)
	}
	if n < 0 {
		n = bytes.Count(s, p) + 1
	}
	out := substr.New(s, n+1)
	c := p[0]
	start := 0
	for i := 0; i+len(p) <= len(s) && len(out.Subs) <= n; i++ {
		if s[i] == c && (len(p) == 1 || bytes.Equal(s[i:i+len(p)], p)) {
			out.Push(start, i)
			start = i + len(p)
			i += len(p) - 1
		}
	}
	out.Push(start, -1)
	return out
}

var ToTitle FreeReplacer = bytes.ToTitle
var ToLower FreeReplacer = bytes.ToLower
var ToUpper FreeReplacer = bytes.ToUpper

type ToTitleSpecial unicode.SpecialCase
type ToLowerSpecial unicode.SpecialCase
type ToUpperSpecial unicode.SpecialCase

func (t ToTitleSpecial) Replace(s []byte) []byte {
	return bytes.ToTitleSpecial(unicode.SpecialCase(t), s)
}

func (t ToLowerSpecial) Replace(s []byte) []byte {
	return bytes.ToLowerSpecial(unicode.SpecialCase(t), s)
}

func (t ToUpperSpecial) Replace(s []byte) []byte {
	return bytes.ToUpperSpecial(unicode.SpecialCase(t), s)
}

type TrimLeft string
type TrimRight string
type Trim string

func (t TrimLeft) Replace(s []byte) []byte {
	return bytes.TrimLeft(s, string(t))
}

func (t TrimRight) Replace(s []byte) []byte {
	return bytes.TrimRight(s, string(t))
}

func (t Trim) Replace(s []byte) []byte {
	return bytes.Trim(s, string(t))
}

type TrimLeftFunc func(int) bool
type TrimRightFunc func(int) bool
type TrimFunc func(int) bool

func (f TrimLeftFunc) Replace(s []byte) []byte {
	return bytes.TrimLeftFunc(s, f)
}

func (f TrimRightFunc) Replace(s []byte) []byte {
	return bytes.TrimRightFunc(s, f)
}

func (f TrimFunc) Replace(s []byte) []byte {
	return bytes.TrimFunc(s, f)
}

var TrimSpace TrimFunc = unicode.IsSpace

type Map func(int) int

func (m Map) Replace(s []byte) []byte {
	return bytes.Map(m, s)
}
